
#pragma once

#include <tbb/parallel_for.h>
#include <tbb/tick_count.h>

#include "Common/ICalculationCore.h"

//Kernel, that interacts bodies from required range of SubVector.
class tbbInteractionKernel
{
	private:
		InterleavedBodies::SubVector *bodies;
	public:
		inline tbbInteractionKernel(InterleavedBodies::SubVector *bodies)
		{ this->bodies = bodies; }
		inline void operator() (const tbb::blocked_range<int> &range) const
		{
			for (int i = range.begin(); i < range.end(); i++)
				for (size_t j = 0; j < bodies->getSize(); j++)
					if (i != j)
						//Interacts i-th body with j-th.
						bodies->interactWith((size_t)i, j);
		}
};

//Kernel, that interacts bodies from required range of SubVector.
class tbbUpdateKernel
{
	private:
		InterleavedBodies::SubVector *bodies;
		float dt;
	public:
		inline tbbUpdateKernel(InterleavedBodies::SubVector *bodies, float dt)
		{
			this->bodies = bodies;
			this->dt = dt;
		}
		inline void operator() (const tbb::blocked_range<int> &range) const
		{
			for (int i = range.begin(); i < range.end(); i++)
			{
				bodies->update((size_t)i, dt);
			}
		}
};

//Version, that uses parallel_for primitive from Intel TBB library.
class tbbCore :public ICalculationCore
{
	public:
		virtual double simulate(InterleavedBodies::SubVector bodies, float dt)
		{
			tbb::tick_count t_start = tbb::tick_count::now();
			tbb::parallel_for(tbb::blocked_range<int>(0, (int)bodies.getSize(), 1),
							  tbbInteractionKernel(&bodies));
			tbb::parallel_for(tbb::blocked_range<int>(0, (int)bodies.getSize(), 1),
							  tbbUpdateKernel(&bodies, dt));
			tbb::tick_count t_end = tbb::tick_count::now();
			return (t_end - t_start).seconds();
		}
};
